package com.example.burstcapture;

import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.media.Image;
import android.media.ImageReader;
import android.util.Log;
import android.util.Range;
import android.view.Surface;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

public class BurstCaptureExpo {
    CaptureRequest.Builder mStillBuilder = null;
    CameraCaptureSession mCaptureSession = null;
    CameraDevice mCameraDevice = null;
    Surface mImageSurface;
    ImageReader mImageReader;
    public BurstCaptureExpo(CameraDevice cameraDevice, CameraCaptureSession captureSession, ImageReader imageReader, CameraCharacteristics characteristics){
        Log.e("TAG", "66666666666 ");
        mCameraDevice = cameraDevice;
        mCaptureSession = captureSession;
        mImageReader = imageReader;
        mImageSurface = mImageReader.getSurface();
        mImageReader.setOnImageAvailableListener(mReaderImgListener, null);

        try {
            mStillBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            mStillBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, CaptureRequest.CONTROL_CAPTURE_INTENT_STILL_CAPTURE);
            mStillBuilder.set(CaptureRequest.CONTROL_AE_MODE, CameraMetadata.CONTROL_AE_MODE_OFF);
            mStillBuilder.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.FLASH_MODE_TORCH); // just in case
            mStillBuilder.addTarget(mImageSurface);
            CaptureRequest request = mStillBuilder.build();
            List<CaptureRequest> requests = new ArrayList<>();

            Range<Integer> iso_range = characteristics.get(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE); // may be null on some devices
            int iso = 800;
            // see https://sourceforge.net/p/opencamera/tickets/321/ - some devices may have auto ISO that's
            // outside of the allowed manual iso range!
            iso = Math.max(iso, iso_range.getLower());
            iso = Math.min(iso, iso_range.getUpper());
            mStillBuilder.set(CaptureRequest.SENSOR_SENSITIVITY, iso );
            mStillBuilder.set(CaptureRequest.SENSOR_FRAME_DURATION, 1000000000L/30);

            int n_half_images = 3/2;
            long base_exposure_time = 22222000;
            long min_exposure_time = base_exposure_time;
            long max_exposure_time = base_exposure_time;
            float expo_bracketing_stops = 2.0f;
            final double scale = Math.pow(2.0, expo_bracketing_stops/(double)n_half_images);
            Range<Long> exposure_time_range = characteristics.get(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE); // may be null on some devices
            if( exposure_time_range != null ) {
                min_exposure_time = exposure_time_range.getLower();
                max_exposure_time = exposure_time_range.getUpper();
            }
            // darker images
            for(int i=0;i<1;i++) {
                long exposure_time = base_exposure_time;
                if( exposure_time_range != null ) {
                    double this_scale = scale;
                    for(int j=i;j<n_half_images-1;j++)
                        this_scale *= scale;
                    exposure_time /= this_scale;
                    if( exposure_time < min_exposure_time )
                        exposure_time = min_exposure_time;
                    mStillBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, exposure_time);
                    Log.e("TAG", " 0000000000 ");
                    requests.add( mStillBuilder.build() );
                }
            }
            Log.e("666666", "scale = "+scale +" base_exposure_time = "+base_exposure_time+" exposure_time = "+base_exposure_time/scale);
            Log.e("TAG", " 33333333333 ");

            // base image
            mStillBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, base_exposure_time);
            requests.add( mStillBuilder.build() );

            // lighter images
            for(int i=0;i<n_half_images;i++) {
                long exposure_time = base_exposure_time;
                if( exposure_time_range != null ) {
                    double this_scale = scale;
                    for(int j=0;j<i;j++)
                        this_scale *= scale;
                    exposure_time *= this_scale;
                    if( exposure_time > max_exposure_time )
                        exposure_time = max_exposure_time;
                    mStillBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, exposure_time);
                    Log.e("TAG", " 1111111111111 ");
                    requests.add( mStillBuilder.build() );
                }
            }
            Log.e("TAG", "n_half_images = "+n_half_images );
            Log.e("666666", "scale = "+scale +" exposure_time = "+(scale * base_exposure_time)+" base_exposure_time = "+base_exposure_time);

//            mCaptureSession.stopRepeating();
            mCaptureSession.captureBurst(requests, null, null);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }

    }

    private int imageIndex = 1;
    private void writeImageToFile() {
        Log.e("TAG", "imageIndex " +imageIndex);
//        String filePath = Environment.getExternalStorageDirectory() + "/DCIM/Camera/001.jpg";
        String filePath = "/data/data/com.example.burstcapture/files/"+imageIndex+".jpg";
        imageIndex++;
        Image image = mImageReader.acquireNextImage();
        if (image == null) {
            return;
        }
        ByteBuffer byteBuffer = image.getPlanes()[0].getBuffer();
        byte[] data = new byte[byteBuffer.remaining()];
        byteBuffer.get(data);
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(new File(filePath));
            fos.write(data);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fos.close();
                fos = null;
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                image.close();
                image = null;
            }
        }
    }

    ImageReader.OnImageAvailableListener mReaderImgListener = new ImageReader.OnImageAvailableListener() {
        @Override
        public void onImageAvailable(ImageReader reader) {
            Log.e("TAG", "onImageAvailable: imageIndex " +imageIndex );
            writeImageToFile();
        }
    };
}
